home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-12-10 | 19.5 KB | 640 lines | [TEXT/ALFA] |
- ## -*-Tcl-*- (install)
- # ###################################################################
- # Vince's Additions - an extension package for Alpha
- #
- # FILE: "cMode.tcl"
- # created: 19/4/96 {4:53:38 pm}
- # last update: 13/11/97 {8:03:47 am}
- # Author: Vince Darley
- # E-mail: <darley@fas.harvard.edu>
- # mail: Division of Applied Sciences, Harvard University
- # Oxford Street, Cambridge MA 02138, USA
- # www: <http://www.fas.harvard.edu/~darley/>
- #
- # ###################################################################
- ##
-
- alpha::mode C 1.1.2 dummyC { *.r } {}
- alpha::mode C++ 1.1.2 dummyC++ \
- {*.H *.c *.h *.cc *.cp *.cpp *.CPP *.C *.pch *.pch++ *.icc *.exp} {}
-
-
- hook::register saveHook modified "C" "C++"
-
- proc dummyC {} {}
- proc dummyC++ {} {}
-
- newPref f elecColon {1} C
- newPref f elecRBrace {1} C
- newPref v leftFillColumn {3} C
- newPref v prefixString {//} C
- newPref f electricSemi {1} C
- newPref f elecLBrace {1} C
- newPref f elecElse {1} C
- newPref f wordWrap {0} C
- newPref v funcExpr {^[^ \t\(#\r/@].*\(.*\)$} C
- newPref v parseExpr {\b([_:\w]+)\s*\(} C
- newPref v wordBreak {[_\w]+} C
- newPref v wordBreakPreface {[^_\w]} C
- newPref f electricTab {1} C
- newPref f autoMark 0 C
- newPref v stringColor green C
- newPref v commentColor red C
- newPref v funcColor yellow C
- newPref v keywordColor blue C
- newPref v CWCompSig CWIE C
- newPref v CWDbgSig MWDB C
- newPref v SymCompSig KAHL C
- newPref v SymDbgSig {◊LSD} C
- newPref f includeMenu {1} C
- newPref f launchIDEifRequired {1} C
- newPref v sourceSuffices { .c } C
- newPref v headerSuffices { .h } C
- newPref v indentComments "code 0" C
- newPref v indentMacros "fixed 0" C
- newPref v IDE 0 C "" [list "CodeWarrior" "Symantec" "none"] index
- newPref f useFasterButWorseIndentation 0 C
- set cCommentRegexp {/\*(([^*]/)|[^*]|\r)*\*/}
- set cPreRegexp {^\#[\t ]*[a-z]*}
- set cKeyWords {
- void break register short enum extern int for if while struct static long continue
- switch case char unsigned double float return else default goto do pascal Boolean
- typedef volatile union auto sizeof size_t
- }
- if {[info exists Cwords]} {set cKeyWords [concat $cKeyWords $Cwords]}
- regModeKeywords -e {//} -b {/*} {*/} -c $CmodeVars(commentColor) -f $CmodeVars(funcColor) -k $CmodeVars(keywordColor) -s $CmodeVars(stringColor) -m {#} C $cKeyWords
-
- #================================================================================
-
- newPref f elecColon {1} C++
- newPref f elecRBrace {1} C++
- newPref v leftFillColumn {3} C++
- newPref v prefixString {//} C++
- newPref f electricSemi {1} C++
- newPref v wordBreak {[\w_]+} C++
- newPref v wordBreakPreface {[^_\w]} C++
- newPref f elecLBrace {1} C++
- newPref f elecElse {1} C++
- newPref f wordWrap {0} C++
- newPref v funcExpr {^([^ \t\(#\r/@].*[ \t]+)?\*?([A-Za-z0-9~_]+(<[^>]*>)?::[-A-Za-z0-9~_+= <>\|\*/]+|[A-Za-z0-9~_]+)[ \t\r]*\(} C++
- newPref v parseExpr {\b([_:\w]+)\s*\(} C++
- newPref f electricTab {1} C++
- newPref f autoMark 0 C++
- newPref v stringColor green C++
- newPref v commentColor red C++
- newPref v keywordColor blue C++
- newPref v funcColor yellow C++
- newPref v CWCompSig CWIE C++
- newPref v CWDbgSig MWDB C++
- newPref v SymCompSig KAHL C++
- newPref v SymDbgSig {◊LSD} C++
- newPref f includeMenu {1} C++
- newPref f launchIDEifRequired {1} C++
- newPref v sourceSuffices { .cc .cp .cpp .c .icc .C } C++
- newPref v headerSuffices { .h .hh } C++
- # These three are pairs:
- # if first item = code, then indent relative to code by given value of second arg
- # if first item = fixed, then force indentation to given level
- newPref v indentComments "code 0" C++
- newPref v indentC++Comments "code 0" C++
- newPref v indentMacros "fixed 0" C++
- newPref v IDE 0 C++ "" [list "CodeWarrior" "Symantec" "none"] index
- newPref f useFasterButWorseIndentation 0 C++
-
- set {c++KeyWords} {
- new delete explicit class friend protected private public template try
- catch throw operator const mutable virtual asm inline this and and_eq
- bitand bitor compl not or or_eq xor xor_eq not_eq wchar_t bool true
- false bool inline mutable static_cast dynamic_cast reinterpret_cast
- typeid using namespace inherited
- }
- if {[info exists {C++words}]} {
- set {c++KeyWords} [concat ${c++KeyWords} ${C++words} $cKeyWords]
- } else {
- set {c++KeyWords} [concat ${c++KeyWords} $cKeyWords]
- }
-
- regModeKeywords -e {//} -b {/*} {*/} -c [set C++modeVars(commentColor)] -f [set C++modeVars(funcColor)] -k [set C++modeVars(keywordColor)] -s [set C++modeVars(stringColor)] -m {#} {C++} ${c++KeyWords}
- unset cKeyWords
- unset {c++KeyWords}
-
- proc C++::DblClick {from to shift option control} {
- if {[regexp {#include.*("|<)(.*)("|>)} [getText [lineStart [getPos]] [nextLineStart [getPos]]] d1 d1 inc]} {
- return [file::tryAndOpen $inc]
- }
-
- select $from $to
- set text [getSelect]
-
- global tagFile
- set lines [grep "^$text'" $tagFile]
- if {[regexp {'(.*)'(.*[^\t])(\t)+∞} $lines dummy one two]} {
- openFileQuietly $one
- set inds [search -s -f 1 -r 0 "$two" 0]
- display [lindex $inds 0]
- eval select $inds
- } else {
- app::launchFore DanR
- AEBuild {'DanR'} DanR {REF } "----" "“$text”"
- }
- }
-
- proc C++::parseFuncs {} {
- global mode sortFuncsMenu
- global funcExpr parseExpr
-
- set pos 0
- while {[set res [search -s -f 1 -r 1 -i 0 -n $funcExpr $pos]] != ""} {
- if {[regexp $parseExpr [getText [car $res] [cadr $res]] dummy word]} {
- lappend m [list $word [car $res]]
- }
- set pos [cadr $res]
- }
- if $sortFuncsMenu {
- regsub -all "\[\{\}\]" [lsort -ignore $m] "" m
- } else {
- regsub -all "\[\{\}\]" $m "" m
- }
- set files ""
- foreach f [getIncludeFiles] {
- lappend files $f -1
- }
- return [concat $files [list "(-" 0] $m]
- }
-
- # for C mode
-
- proc C::DblClick {args} { eval C++::DblClick $args }
-
- proc C:parseFuncs {} {
- return [C++::parseFuncs]
- }
-
-
- #############################################################################
- # #
- # Stuff above this point has only minor modifications from the original #
- # "cMode.tcl", stuff below is largely or totally new. #
- # #
- #############################################################################
-
- # ◊◊◊◊ File marking ◊◊◊◊ #
-
- ##
- # -------------------------------------------------------------------------
- #
- # "C++::MarkFile" --
- #
- # Improved version which handles templates, operators etc.
- # Makes use of the new mark menu in Alpha 6.5 which can handle
- # more weird characters. Handles most 'operator =+-*...' functions
- # for C++
- #
- # Better marking of templates recently added.
- # -------------------------------------------------------------------------
- ##
- proc C++::MarkFile {} {
- if {[file extension [win::Current]] == ".exp"} { return }
- set pos 0
- set markExpr {^([^ \t\(#\r/@\*].*[ \t]+\*?)?([A-Za-z0-9~_]+(<[^>]*>)?(::)?[-A-Za-z0-9~_+= <>\|\*/]+|[A-Za-z0-9~_]+)[ \t\r]*\(}
- while {![catch {search -s -f 1 -r 1 -m 0 -i 0 "$markExpr" $pos} res]} {
- if [catch {search -s -f 0 -r 1 -m 0 -l [lindex $res 0] -i 0 \
- {[ *][a-zA-Z]} [set pos [expr [lindex $res 1] + 1]]} start] {
- continue
- }
- set start [lindex $start 0]
- set thistext [getText $start $pos]
- #regexp doesn't like carriage returns or tabs
- #if the open paren was the last character on the line the selected text
- #included the last carriage return as well
- #trim this off now that it is changed into a space
- regsub -all "\[\r\t\]" [string trimright $thistext] " " thistext
- if {[regexp {([A-Za-z0-9~_]+(<[^>]*>)?(::)?[-A-Za-z0-9~_+= <>\|\*/]+|[A-Za-z0-9~_]+)[ \t]*\(} $thistext dummy word]} {
- if { [string first "::" $word] != -1 } {
- regsub {(<\w+>)?::} $word " " it
- set l [lindex $it 0]
- if { $l == [lindex $it 1] } {
- set word "Construct '$l'"
- } elseif { "~$l" == [lindex $it 1] } {
- set word "Destruct '$l'"
- }
- }
- set inds($word) [lineStart [expr $start - 1]]
- }
- }
- if {[info exists inds]} {
- foreach f [lsort -ignore [array names inds]] {
- set next [nextLineStart $inds($f)]
- set it $f
- if {[string length $it] > 57} { set it "[string range $it 0 53]..." }
- setNamedMark "${it}" "$inds($f)" $next $next
- }
- }
- }
-
- proc C::MarkFile {} { C++::MarkFile }
-
-
- # ◊◊◊◊ Indentation routines ◊◊◊◊ #
-
- proc C::indentLine {} {C++::indentLine}
-
- ##
- # -------------------------------------------------------------------------
- #
- # "C++indentLine" --
- #
- # More sophisticated version of Pete's. Handles things like '(...)'
- # expressions split over multiple lines, if/elseif/else both with and
- # without curly braces, multiple line stream manipulation with '<<'
- # or '>>', C and C++ style comments, ... Assumes indentation is '4'
- # but any tab-size may be used.
- #
- # Current bugs: multi-line ',' separated lists are poorly indented.
- #
- # Problems:
- # matchIt's limit doesn't seem to work, so if there is no match and we're
- # in a large file, we wait up to seconds sometimes. Alpha bug.
- #
- # Currently checking whether we're in a /*...*/ comment is quite
- # time consuming. It would be nice if Alpha supplied a hook to do
- # this for us.
- #
- # Results:
- # Indents the current line correctly ;-) for C, C++ coding
- #
- # --Version--Author------------------Changes-------------------------------
- # 1.0 Pete Keleher original
- # 2.0 <darley@fas.harvard.edu> updated as described above.
- # 2.1 <darley@fas.harvard.edu> faster, better, uses positions not strings
- # 2.2 <darley@fas.harvard.edu> uses 'correctIndentation' sub proc
- # -------------------------------------------------------------------------
- ##
- proc C++::indentLine {} {
- global gotoEol
- set gotoEol 0
-
- # preliminaries
- set beg [lineStart [getPos]]
- # are we in a C comment /*...*/ if so indent specially and return
- # we really need to work out how to put this in 'correctIndentation'
- if ![catch {C_inCComment $beg} comment] {
- set fChar [search -s -f 1 -r 1 {[^ \t\r]} $beg]
- if { [lookAt [lindex $fChar 0]] == "*" } {
- return [eval C_indentCommentLine $beg $comment]
- }
- }
- regexp {^([ \t]*)([^ \t]+)} [getText $beg [nextLineStart $beg]] \
- "" white rest
- set len [string length $white]
- # get indentation level
- set lwhite [text::indentOf [C++::correctIndentation [getPos] $rest]]
- if {$white != $lwhite} {
- replaceText $beg [expr $beg +$len] $lwhite
- }
- if $gotoEol {
- goto [expr [nextLineStart $beg] -1]
- } else {
- goto [expr $beg + [string length $lwhite]]
- }
-
- }
- proc C::correctIndentation {args} {eval C++::correctIndentation $args}
- ##
- # -------------------------------------------------------------------------
- #
- # "C++::correctIndentation" --
- #
- # Known bugs:
- #
- # Lines which contain a URL with :// embedded tend to be considered
- # a ':' followed by a comment, and are indented as if they were
- # part of a 'case://comment' statement which is wrong.
- # -------------------------------------------------------------------------
- ##
- proc C++::correctIndentation {pos {nextword ""}} {
- global gotoEol
- # preliminaries
- set beg [lineStart $pos]
- set nextCh [string range $nextword 0 3]
- set nextC [string index $nextCh 0]
- set nextP [string range $nextCh 0 1]
- # check for forced indentation of C, C++ comments and '#' macros
- set ind "code 0"
- switch -- $nextC {
- "\#" {
- global indentMacros
- set ignore_trailers ""
- set ind $indentMacros
- }
- "/" {
- global indentComments indentC++Comments
- set ignore_trailers ""
- if {$nextP == "/*"} {set ind $indentComments}
- if {$nextP == "//"} {set ind ${indentC++Comments}}
- }
- }
- if {[lindex $ind 0] == "fixed" } {
- # force indentation to given level
- return [lindex $ind 1]
- }
-
- # (1) first we get the indent of the last line:
- # this may involve looking back a fair way
- set lst [C_prevCodeIndent [expr $beg -1]]
-
- set pstart [lindex $lst 0]
- set lwhite [posX [expr [lindex $lst 1] - 1]]
- # have we just finished an if-elseif-else with no '{}'?
- if {$nextCh == "else"} {set iselse 1} else { set iselse 0}
- if ![C_isLineNBI $pstart] {
- incr lwhite [C_recurseNoBraceIndent $pstart 0 $iselse]
- }
- if { [set multi [C_isLineMulti $pstart]] != "-1" } {
- set lwhite $multi
- }
-
- # (2) now we indent this line accordingly
-
- set pbeg [prevLineStart $beg]
- set backpos [nextLineStart [lindex $lst 0]]
- # is there a comment at the end of the line? if so scan back to the character we want
- if ![catch {search -s -f 0 -r 1 -l $pbeg {//[^\r]*\r} $beg} compos] {
- set compos [lindex $compos 0]
- if { $compos > $pbeg } {
- set backpos [expr $compos +1]
- }
- }
- global indentationAmount
- if {[incr backpos -2] > 0} {
- set lst [search -s -f 0 -r 1 -m 0 {[^ \t\r]} $backpos]
- switch -- [lookAt [lindex $lst 0]] {
- "\{" {
- incr lwhite $indentationAmount
- }
- ":" {
- incr lwhite [expr $indentationAmount /2]
- }
- "\)" {
- # see if we're in a if-elseif-else with no '{}' and indent
- if [C_isLineNBI $pstart] {
- incr lwhite $indentationAmount
- }
- }
- "e" {
- if { [getText [expr [lindex $lst 0] -3] [expr [lindex $lst 0]+1]] == "else" } {
- if [C_isLineNBI $pstart] {
- incr lwhite $indentationAmount
- }
- }
-
- }
- }
- }
-
- switch -- $nextC {
- "\}" {
- incr lwhite [expr -$indentationAmount]
- }
- "<" -
- ">" {
- # indent for '<<' and '>>' in multi-line C++ stream manipulation
- if {$nextP == "<<" || $nextP == ">>"} {
- set strm [search -s -f 1 -r 1 "^\[^${nextC}\]+${nextP}" $pbeg]
- set lwhite [posX [expr [lindex $strm 1] -2]]
- set gotoEol 1
- }
- }
- }
- # Check if we're in a multi-line '(.....)' if so align to start
- global useFasterButWorseIndentation
- if {!$useFasterButWorseIndentation && ![catch {matchIt ")" $beg 200} paren]} {
- set lwhite [posX [expr $paren+1]]
- set gotoEol 1
- }
-
- # [regexp {:[ \t]*(//[^\r]*)?\r} $text]
- if {[regexp {^\w+:} $nextword] && $lwhite > 3 \
- && ![info exists ignore_trailers]} {
- incr lwhite [expr -$indentationAmount/2]
- }
- # get indentation level
- return [incr lwhite [lindex $ind 1]]
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "recurseNoBraceIndent" --
- #
- # Scans back until we no longer have a 'no brace indent'.
- # A 'no brace indent' is a 'for', 'if' etc which didn't use
- # '{ ... }'
- # -------------------------------------------------------------------------
- ##
- proc C_recurseNoBraceIndent {pos offset {iselse 0}} {
- set pos [prevLineStart $pos]
- if [C_isLineNBI $pos] {
- if $iselse {
- set p [text::firstNonWsPos $pos]
- set t [getText $p [expr $p +10]]
- if [regexp {(else[ \t]+)?if.*} $t] {
- return [expr $offset -4]
- }
- }
- return [C_recurseNoBraceIndent $pos [incr offset -4] $iselse]
- }
- return $offset
-
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "isLineNBI" --
- #
- # Tests if the given line is a 'no brace indent'.
- # -------------------------------------------------------------------------
- ##
- set C_recNBI {^[ \t]*((\}?[ \t]*(if|else[ \t]+if)|for)[ \t]*\(.*\)|\}?[ \t]*else)[ \t]*(//[^\r]*)?[ \t]*$}
- proc C_isLineNBI {pos} {
- global C_recNBI
- if {![catch {search -s -f 1 -r 1 -l [expr [nextLineStart $pos]+2] $C_recNBI $pos} ifelse] } {
- if { $pos == [lindex $ifelse 0] } {
- return 1
- }
- }
- return 0
- }
-
- # use 'catch' to call this proc: error = no, otherwise returns st,end pos
- proc C_inCComment {pos} {
- set cS [search -s -f 0 -r 0 -l [expr $pos -1000] "/*" $pos]
- set cE [search -s -f 1 -r 0 -l [expr $pos +1000] "*/" [lindex $cS 1]]
- if { $pos >= [lindex $cE 1] } {
- error "No"
- } else {
- return "[lindex $cS 0] [lindex $cE 1]"
- }
- }
-
- # look for '<<' and '(...)' multi lines.
- proc C_isLineMulti {pos} {
- # look for multi-line '(...)'
- if { ![catch {search -s -f 0 -r 1 -l $pos {\).*$} [nextLineStart $pos]} paren] \
- && [nextLineStart $pos] == [expr [lindex $paren 1]+1] } {
- if [catch {matchIt ")" [expr [lindex $paren 0] -1]} realStart] {
- return -1
- }
- if { [lineStart $realStart] != [lineStart [lindex $paren 0]] } {
- set lst [search -s -f 0 -r 1 -i 0 {^[ \t]*[^ \t\r]} $realStart]
- return [posX [expr [lindex $lst 1] - 1]]
- }
- }
- # look for multi-line '<<' or '>>'
- set p $pos
- while {![catch {search -s -f 1 -r 1 -l [nextLineStart $p] {^[ \t]*(<<|>>)} $p} strm] } {
- set p [prevLineStart $p]
- }
- if { $p != $pos } {
- set lst [search -s -f 1 -r 1 -i 0 {^[ \t]*[^ \t\r]} $p]
- return [posX [expr [lindex $lst 1] - 1]]
- }
-
- return -1
-
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "C_indentCommentLine" --
- #
- # Indents a line within a multi-line /* ... */ comment correctly.
- # -------------------------------------------------------------------------
- ##
- proc C_indentCommentLine {beg cS cE} {
- set lwhite [minSpaceForm [getText [lineStart $cS] $cS]]
- if { $beg != [lineStart [lindex $cE 0]] || [text::firstNonWs [expr $beg -1]] == "*" } {
- append lwhite " "
- }
-
- set text [getText $beg [nextLineStart $beg]]
- regexp {^[ \t]*} $text white
- set len [string length $white]
- if {$white != $lwhite} {
- replaceText $beg [expr $beg + $len] $lwhite
- }
- goto [expr $beg + [string length $lwhite] +1]
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "C_prevCodeIndent" --
- #
- # Find the indent of the previous line
- # - If it's the start of the file, return 0 0 (special case)
- # else
- # - if it's a C++ comment, keep looking backwards (so you can offset
- # C++ comments if you so desire)
- # - if it's a C comment, get the indentation of the '/*' not some
- # intermediate point.
- # -------------------------------------------------------------------------
- ##
- proc C_prevCodeIndent {pos} {
- if {[catch {search -s -m 0 -f 0 -r 1 -i 0 {^[ \t]*[^ \t\r]} $pos} p] || $p == "0 0" } {
- return "0 1"
- } else {
- set pp [doubleLookAt [expr [lindex $p 1] -1]]
- if { $pp == "//" } {
- return [C_prevCodeIndent [expr [lindex $p 0]-1]]
- } elseif { [string index $pp 0] == "#" } {
- return [C_prevCodeIndent [expr [lindex $p 0]-1]]
- } elseif { $pp == "*/" } {
- return [C_prevCodeIndent [lindex \
- [search -s -f 0 -r 0 "/*" [expr [lindex $p 0]-1]] 0]]
- } elseif { ![catch {set comment [C_inCComment [lindex $p 0]]} ] } {
- return [C_prevCodeIndent [expr [lineStart [lindex $comment 0]]-1]]
- #return [text::indentation [lindex $comment 0]] (old style)
- }
- }
- return $p
- }
-
-
- # ◊◊◊◊ Electric routines ◊◊◊◊ #
-
- proc C::carriageReturn {} {C++::carriageReturn}
- proc C::OptionTitlebar {} {C++::OptionTitlebar}
- proc C::OptionTitlebarSelect {item} {C++::OptionTitlebarSelect $item}
-
- ##
- # -------------------------------------------------------------------------
- #
- # "C++::carriageReturn" --
- #
- # Called by the general routine 'carriageReturn'. We know no selection
- # exists, and we are not inside a block comment.
- # -------------------------------------------------------------------------
- ##
- proc C++::carriageReturn {} {
- if {[lookAt [expr [getPos] - 1]] == ":"} {
- if { [lookAt [getPos]] == "\r" } {
- bind::IndentLine
- endOfLine
- insertText "\r"
- } else {
- set pos [getPos]
- endOfLine
- set t [getText $pos [getPos]]
- replaceText $pos [getPos] ""
- bind::IndentLine
- endOfLine
- insertText "\r"
- insertText $t
- }
- } else {
- insertText "\r"
- }
- if [catch {bind::IndentLine}] { beep; message "bug in cmode carriage return" }
- }
-
- proc C++::OptionTitlebar {} {
- if {![catch {C++::tryIDEget} ret] && ![regexp {\(No} $ret] } { return $ret }
- # else just scan through, provided the scan will function
- getWinInfo a ; if {$a(platform) != "mac"} { return "" }
- set cid [scancontext create]
- set lines {}
- scanmatch $cid {#.*include.*(<|")(.*)(>|")} {lappend lines $matchInfo(submatch1)}
- set fid [open [win::Current] "r"]
- scanfile $cid $fid
- close $fid
- scancontext delete $cid
- return [lsort -ignore $lines]
- }
-
- proc C++::OptionTitlebarSelect {fname} {
- C++::tryIDEedit $fname
- }
-
- proc C++::tryIDEget {} {
- global IDE
- switch $IDE {
- 1 {thinkGetIncludeFiles}
- 0 {cw::getIncludeFiles}
- 2 {error "No IDE at all!"}
- }
- }
-
- proc C++::tryIDEedit {fname} {
- global IDE
- switch $IDE {
- 1 {thinkEditIncludeFile $fname}
- 0 {cw::editIncludeFile $fname}
- 2 {error "No IDE at all!"}
- }
- }
-
-